package com.naiterui.ehp.bs.esign.service.impl;


import java.util.Date;
import java.util.List;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.bo.esign.DigitalCertificateBO;
import com.naiterui.ehp.bp.bo.esign.EsignNoSecretBO;
import com.naiterui.ehp.bp.bo.esign.EsignPharmacistBO;
import com.naiterui.ehp.bp.bo.esign.NoSecretParamBO;
import com.naiterui.ehp.bp.bo.esign.PreSignResultBO;
import com.naiterui.ehp.bp.bo.esign.WillTypeBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.constants.EsignServiceProviderEnum;
import com.naiterui.ehp.bp.support.exception.BaseExceptionCodes;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.utils.beans.BeanUtil;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.encypt.AESUtil;
import com.naiterui.ehp.bp.utils.encypt.GengerCode;
import com.naiterui.ehp.bp.utils.encypt.MD5;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import com.naiterui.ehp.bs.esign.api.ISignService;
import com.naiterui.ehp.bs.esign.api.bjca.vo.SignFileUploadVO;
import com.naiterui.ehp.bs.esign.api.bjca.vo.SignUserParamVO;
import com.naiterui.ehp.bs.esign.api.scca.entity.EsignSccaCert;
import com.naiterui.ehp.bs.esign.api.scca.repository.IEsignSccaCertRepository;
import com.naiterui.ehp.bs.esign.api.vo.SignAccountVO;
import com.naiterui.ehp.bs.esign.api.vo.SignUserInfoVO;
import com.naiterui.ehp.bs.esign.api.vo.UserInfoParamVO;
import com.naiterui.ehp.bs.esign.entity.mysql.EsignDoctor;
import com.naiterui.ehp.bs.esign.entity.mysql.EsignNoSecret;
import com.naiterui.ehp.bs.esign.entity.mysql.EsignPharmacist;
import com.naiterui.ehp.bs.esign.exception.ExceptionCodes;
import com.naiterui.ehp.bs.esign.feign.IPharmacistFeignClient;
import com.naiterui.ehp.bs.esign.repository.mysql.IEsignRecomEsignDoctorRepository;
import com.naiterui.ehp.bs.esign.repository.mysql.IEsignRecomEsignPharmacistRepository;
import com.naiterui.ehp.bs.esign.service.IEsignService;
import com.naiterui.ehp.bs.esign.service.INoSecretService;
import com.naiterui.ehp.bs.esign.service.IPharmacistService;
import com.naiterui.ehp.bs.esign.utils.Constants;
import com.naiterui.ehp.bs.esign.utils.EsignWillTypeEnum;
import com.naiterui.ehp.bs.esign.utils.SealImageUtil;
import com.naiterui.ehp.bs.esign.utils.SignUtils;
import com.naiterui.ehp.bs.esign.vo.esign.PreSignResultVO;

import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @Description 医生签名信息
 * @Author gongbaoqiang
 * @Date 2020/1/2 10:44 AM
 */
@Slf4j
@Service
public class EsignPharmacistServiceImpl implements IPharmacistService {

    @Autowired
    private ISignService signService;

    @Autowired
    private IPharmacistFeignClient pharmacistFeignClient;

    @Autowired
    private IEsignService esignService;

    @Autowired
    private IEsignRecomEsignPharmacistRepository recomEsignPharmacistRepository;

    @Autowired
    private IEsignRecomEsignDoctorRepository recomEsignDoctorRepository;

    @Autowired
    private INoSecretService noSecretService;

    @Autowired
    private IEsignSccaCertRepository esignSccaCertRepository;

    @Value("${file.base.prefix}")
    private String fileBasePrefix;
    @Value("${naiterui.scca-risk-warning-notice}")
    private String informPath;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public EsignPharmacist addUserPh(SignUserParamVO userParamVO) throws BusinessException {
        String idCard = userParamVO.getIdCard();
        //药师需要调用签名生成服务
        UserInfoParamVO userInfoParamVO = UserInfoParamVO.builder()
            .idNo(idCard)
                .name(userParamVO.getName())
                .phone(userParamVO.getPhone())
                .organ(userParamVO.getHospital())
                .cerType(UserInfoParamVO.CER_TYPE_IDCARD)
                .title(Constants.USER_TITLE_PH)
            .pin(userParamVO.getPwd())
                .sealFlag(StringUtils.isBlank(userParamVO.getSealImagePath()) ? true : false)
                .build();

        EsignPharmacist pharmacistUser = recomEsignPharmacistRepository.findFirstByPharmacistId(userParamVO.getUserId());
        if (pharmacistUser == null) {
            pharmacistUser = new EsignPharmacist();
            pharmacistUser.setPharmacistId(userParamVO.getUserId());
            String salt = GengerCode.generateSaltCode();
            pharmacistUser.setSalt(salt);
            pharmacistUser.setType(EsignPharmacist.TYPE_NORMAL_USER);
            pharmacistUser.setAuditLevel(EsignPharmacist.AUDIT_LEVEL_FINAL);

            pharmacistUser.setStatus(EsignPharmacist.STATUS_NORMAL);
            pharmacistUser.setCreatedAt(new Date());
            pharmacistUser.setCreatedBy("system");

        }

        SignUserInfoVO userInfoVO = null;
        if (pharmacistUser.getOpenId() == null) {
            userInfoVO = signService.registAccount(userInfoParamVO);
        } else {
            userInfoParamVO.setOpenId(pharmacistUser.getOpenId());
            userInfoVO = signService.updateAccount(userInfoParamVO);
        }

        //原始图片路径
        String sealImagePath = userParamVO.getSealImagePath();
        //修饰图片路径
        String newPath = "";
        if (userInfoParamVO.isSealFlag()) {
            //对三方图片进行裁剪
            newPath = SealImageUtil.cutSealImage(Constants.BASE_PATH + sealImagePath);
            newPath = newPath.replace(Constants.BASE_PATH, "");
        } else {
            newPath = sealImagePath;
        }
        pharmacistUser.setSealImage(SealImageUtil.setSealImage(newPath));
        pharmacistUser.setName(userParamVO.getName());
        pharmacistUser.setIdCard(idCard);
        pharmacistUser.setPhone(userParamVO.getPhone());
        pharmacistUser.setOpenId(userInfoVO.getOpenId());
        if(userParamVO.getAuthStatus() == null){
            pharmacistUser.setAuthStatus(EsignPharmacist.AUTH_STATUS_DEF);
        }else{
            pharmacistUser.setAuthStatus(userParamVO.getAuthStatus());
        }
        pharmacistUser.setChangedAt(new Date());
        pharmacistUser.setChangedBy("system");
        pharmacistUser.setNoSecret(0);

        if (pharmacistUser.getId() == null) {
            recomEsignPharmacistRepository.save(pharmacistUser);
        } else {
            recomEsignPharmacistRepository.update(pharmacistUser);
        }

        //药师表同步保存签名图片
//        pharmacistFeignClient.perfectSealImage(sealImagePath, pharmacistUser.getPharmacistId());

        return pharmacistUser;
    }


    @Override
    public String getRealnameCode(String name, String phone, String idCard) throws BusinessException {
        EsignDoctor esignDoctor =
            recomEsignDoctorRepository.findFirstByIdCardAndAuthStatus(idCard, EsignDoctor.AUTH_STATUS_PASS);
        if (esignDoctor != null) {
            throw new BusinessException(ExceptionCodes.ESIGN_AUTH_IDCARD_EXISTS);
        }
        return signService.getRealnameCode(name, phone, idCard);
    }

    @Override
    public boolean checkRealname(String phone, String code) throws BusinessException {
        //重复提交实名的同样回写实名状态
        signService.checkRealnameCode(phone, code);
        EsignPharmacist pharmacist = recomEsignPharmacistRepository.findFirstByPhone(phone);
        if (null != pharmacist) {
            this.setRealnameStatus(pharmacist);
        } else {
            SignUserParamVO signUserParamVO = pharmacistFeignClient.getUserByPhone(phone);
            addUserPh(signUserParamVO);
        }
        // EsignPharmacist pharmacist = recomEsignPharmacistRepository.findFirstByPhone(phone);
        // this.setRealnameStatus(pharmacist);
        return true;
    }

    private void setRealnameStatus(EsignPharmacist pharmacist) throws BusinessException {
        //重复提交实名的同样回写实名状态
        int auditStatus = EsignPharmacist.AUTH_STATUS_PASS;
        pharmacist.setAuthStatus(auditStatus);
        pharmacist.setChangedAt(new Date());
        //解决乐观锁报错问题
        pharmacist.setVersion(recomEsignPharmacistRepository.get(pharmacist.getId()).getVersion());
        recomEsignPharmacistRepository.update(pharmacist);
        // 获取激活厂商
        EsignServiceProviderEnum service = signService.getCurrentServiceProvider();
        if (EsignServiceProviderEnum.SCCA.equals(service)) {
            // 更新药师信息
            pharmacistFeignClient.esignAuth(pharmacist.getPharmacistId(), auditStatus, "成功");
        }
    }

    @Override
    public EsignPharmacistBO getEsignInfo(Long pharmacistId) throws BusinessException {
        EsignPharmacistBO bo = new EsignPharmacistBO();
        EsignPharmacist pharmacist = recomEsignPharmacistRepository.findFirstByPharmacistId(pharmacistId);
        if (pharmacist == null) {
            log.info("药师账号不存在：pharmacistId={}", pharmacistId);
        } else {
            BeanUtil.copyProperties(pharmacist, bo);
            bo.setSealImage(ConfigUtil.getString("ehp.domain.img") + bo.getSealImage());
        }
        return bo;
    }

    @Override
    public void updateSealData(Long pharmacistId, String sealImage) throws BusinessException {
        EsignPharmacist pharmacist = this.getEsignPharmacist(pharmacistId);
        pharmacist.setSealImage(SealImageUtil.setSealImage(sealImage));
        pharmacist.setChangedAt(new Date());
        recomEsignPharmacistRepository.update(pharmacist);
    }

    @Override
    public List<WillTypeBO> getWillTypes(Long pharmacistId) throws BusinessException {

        EsignPharmacist pharmacist = this.getEsignPharmacist(pharmacistId);
        // 未设置密码移除掉，密码签署方式
        Boolean signPwdSet = RedisUtil.keyOps().existsKey(Constants.ESIGN_PWD_CACHE_NEWEST + pharmacist.getPhone());
        List<WillTypeBO> willTypes = esignService.getWillTypes(pharmacist.getWillType());
        if(!signPwdSet){
            // 只移除E签宝密码签署方式(四川CA目前只有密码暂不移除)
            willTypes.removeIf(w -> EsignWillTypeEnum.WUKONG_SIGN_PWD.getValue().equals(w.getValue()));
        }
        return willTypes;
    }

    @Override
    public void updateWillType(Long pharmacistId, String willType) throws BusinessException {
        EsignPharmacist pharmacist = this.getEsignPharmacist(pharmacistId);
        pharmacist.setWillType(willType);
        pharmacist.setChangedAt(new Date());
        recomEsignPharmacistRepository.update(pharmacist);
    }

    @Override
    public void updateSignPassword(Long pharmacistId, String password) throws BusinessException {
        EsignPharmacist pharmacist = this.getEsignPharmacist(pharmacistId);
        String phone = pharmacist.getPhone();
        String md5Pwd = MD5.md5(password);
        String lastedPwd = RedisUtil.valueOps().getString(Constants.ESIGN_PWD_CACHE_LASTED + phone);
        //密码与上次一致，则不向签名服务同步
        if (md5Pwd.equals(lastedPwd)) {
            return;
        }

        //存储签名密码
        pharmacist.setAuthPwd(AESUtil.encrypt(CommonConstant.ESIGN_NO_SECRET_KEY, password));
        this.recomEsignPharmacistRepository.update(pharmacist);
        RedisUtil.valueOps().setString(Constants.ESIGN_PWD_CACHE_PRE + phone, md5Pwd);
        RedisUtil.valueOps().setString(Constants.ESIGN_PWD_CACHE_NEWEST + phone, md5Pwd);
        RedisUtil.valueOps().setString(Constants.ESIGN_PWD_CACHE_LASTED + phone, md5Pwd);

        // 获取激活厂商
        EsignServiceProviderEnum service = signService.getCurrentServiceProvider();
        if (EsignServiceProviderEnum.SCCA.equals(service)) {
            log.info("设置密码-四川CA设置密码触发实名 pharmacistId:{}", pharmacistId);
            // 四川CA设置密码时同时设置实名状态
            this.setRealnameStatus(pharmacist);
        }
        this.signService.resetSignPwd(pharmacist.getOpenId(), password);
    }

    @Override
    public SignAccountVO getSignAccount(Long pharmacistId, String unionId) throws BusinessException {

        EsignPharmacist pharmacist = recomEsignPharmacistRepository.findFirstByPharmacistId(pharmacistId);
        if (pharmacist == null || !EsignPharmacist.AUTH_STATUS_PASS.equals(pharmacist.getAuthStatus())) {
            throw new BusinessException(ExceptionCodes.ESIGN_REALNAME_NOT_AUTH);
        }
        String openId = Optional.ofNullable(pharmacist).map(EsignPharmacist::getOpenId).orElse(null);
        String sealImage = Optional.ofNullable(pharmacist).map(EsignPharmacist::getSealImage).orElse(null);

        if (StringUtils.isBlank(openId)) {
            log.warn("电子签名用户不存在，或三方用户标识不存在 signUserId:{}", pharmacistId);
            throw new BusinessException(ExceptionCodes.ESIGN_USER_SYNC_FAILED);
        }

        //预置签名
        String sealData = null;
        if (StringUtils.isNotBlank(sealImage)) {
            sealData = SignUtils.encodeBase64File(Constants.BASE_PATH + sealImage);
        }

        return SignAccountVO.builder()
                            .openId(openId)
                            .sealImage(sealImage)
                            .sealData(sealData)
                            .requestFrom(SignAccountVO.REQUEST_FROM_APP)
                            .willType(pharmacist.getWillType())
                            .name(pharmacist.getName())
                            .phone(pharmacist.getPhone())
                            .idNo(pharmacist.getIdCard())
                            .bizId(unionId)
                            .userType(Constants.USER_TITLE_PH)
                            .build();
    }

    @Override
    public Integer getNoSecretByPharmacistId(Long pharmacistId) throws BusinessException {
        EsignPharmacist esignPharmacist = recomEsignPharmacistRepository.findFirstByPharmacistId(pharmacistId);
        if (esignPharmacist == null) {
            log.info("药师签名账号不存在: pharmacistId={}", pharmacistId);
            throw  new BusinessException(ExceptionCodes.ESIGN_DOCTOR_USER_NOT_EXISTS);
        }
        return Optional.ofNullable(esignPharmacist.getNoSecret()).orElse(0);
    }

    @Override
    public EsignNoSecretBO getEsignNoSecretByPharmacistId(Long pharmacistId) throws BusinessException {
        EsignPharmacist esignPharmacist = recomEsignPharmacistRepository.findFirstByPharmacistId(pharmacistId);
        if (esignPharmacist == null) {
            log.info("药师签名账号不存在: pharmacistId={}", pharmacistId);
            throw  new BusinessException(ExceptionCodes.ESIGN_DOCTOR_USER_NOT_EXISTS);
        }
        Integer noSecret = Optional.ofNullable(esignPharmacist.getNoSecret()).orElse(0);
        boolean b = true;
        if(noSecret==0){
            b = false;
        }
        return EsignNoSecretBO.builder().pin(esignPharmacist.getAuthPwd()).noSecret(b).build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PreSignResultBO preNoSecret(Long pharmacistId) throws BusinessException {
        EsignNoSecret esignNoSecret = noSecretService.saveEsignNoSecret(pharmacistId, 1);
        String bizSn = esignNoSecret.getId().toString();
        return this.preSignRecipelPh(pharmacistId, bizSn);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setNoSecret(NoSecretParamBO paramBO) throws BusinessException {
        // 获取激活厂商
        EsignServiceProviderEnum service = signService.getCurrentServiceProvider();
        if (EsignServiceProviderEnum.SCCA.equals(service)) {
            if (paramBO.getNoSecret() && StringUtils.isBlank(paramBO.getAuthPwd())) {
                throw new BusinessException(BaseExceptionCodes.PARAM_ERROR);
            }
        }
        EsignPharmacist esignPharmacist =
            recomEsignPharmacistRepository.findFirstByPharmacistId(paramBO.getSignUserId());
        if (esignPharmacist == null) {
            log.info("药师签名账号不存在: pharmacistId={}", paramBO.getSignUserId());
            throw  new BusinessException(ExceptionCodes.ESIGN_DOCTOR_USER_NOT_EXISTS);
        }
        if (!paramBO.getNoSecret()) {
            recomEsignPharmacistRepository.updateNoSecretAuthPwd(0, esignPharmacist.getAuthPwd(),
                esignPharmacist.getId());
            return;
        }
        // this.signPh(pharmacistId, authPwd);
        this.signPhNoSecret(paramBO);
        if (StringUtils.isNotBlank(paramBO.getAuthPwd())) {
            esignPharmacist.setAuthPwd(AESUtil.encrypt(CommonConstant.ESIGN_NO_SECRET_KEY, paramBO.getAuthPwd()));
        }
        recomEsignPharmacistRepository.updateNoSecretAuthPwd(1, esignPharmacist.getAuthPwd(),
            esignPharmacist.getId());
    }

    @Override
    public void saveSealImg(Long pharmacistId, String sealImgPath) throws BusinessException {
        EsignPharmacist pharmacist = recomEsignPharmacistRepository.findFirstByPharmacistId(pharmacistId);
        if (null != pharmacist) {
            pharmacist.setSealImage(SealImageUtil.setSealImage(sealImgPath));
            pharmacist.setChangedAt(new Date());
            recomEsignPharmacistRepository.update(pharmacist);
        }
    }

    @Override
    public DigitalCertificateBO getDigitalCertificate(Long pharmacistId) throws BusinessException {
        EsignPharmacist esignPharmacist = recomEsignPharmacistRepository.findFirstByPharmacistId(pharmacistId);
        if (null == esignPharmacist) {
            throw new BusinessException(ExceptionCodes.ESIGN_PHARMACIST_USER_NOT_EXIST);
        }
        // 获取激活厂商
        EsignServiceProviderEnum service = signService.getCurrentServiceProvider();
        if (EsignServiceProviderEnum.ESIGN_WUKONG_SDK.equals(service)) {
            return DigitalCertificateBO.builder().username(esignPharmacist.getName())
                .idCard(IdcardUtil.hide(esignPharmacist.getIdCard(), 7, 15)).issuingAuthority("杭州天谷信息科技有限公司")
                .sealCertNumber(StrUtil.hide(esignPharmacist.getOpenId(), 7, 25))
                .issuingTime(DateUtil.formatDate(esignPharmacist.getCreatedAt(), DateUtil.DATE_FORMAT_CN))
                .validUntil("永久").build();
        }
        if (EsignServiceProviderEnum.SCCA.equals(service)) {
            String openId = esignPharmacist.getOpenId();
            EsignSccaCert sccaCert = esignSccaCertRepository.findByOpenId(openId);
            return DigitalCertificateBO.builder().username(esignPharmacist.getName())
                .idCard(IdcardUtil.hide(esignPharmacist.getIdCard(), 7, 15)).issuingAuthority("四川省数字证书认证管理中心有限公司")
                .sealCertNumber(StrUtil.hide(sccaCert.getCertSerialnumber(), 7, 25))
                .issuingTime(DateUtil.formatDate(esignPharmacist.getCreatedAt(), DateUtil.DATE_FORMAT_CN))
                .validUntil(DateUtil.formatDate(sccaCert.getNotAfter(), DateUtil.DATE_FORMAT_CN)).build();
        }
        return DigitalCertificateBO.builder().username(esignPharmacist.getName())
            .idCard(IdcardUtil.hide(esignPharmacist.getIdCard(), 7, 15)).build();
    }

    @Override
    public void checkPharmacistRealname(String idCard) throws BusinessException {
        EsignDoctor esignDoctor =
            recomEsignDoctorRepository.findFirstByIdCardAndAuthStatus(idCard, EsignDoctor.AUTH_STATUS_PASS);
        if (esignDoctor != null) {
            throw new BusinessException(ExceptionCodes.ESIGN_AUTH_IDCARD_EXISTS);
        }
    }

    private PreSignResultBO preSignRecipelPh(Long userId, String bizSn) throws BusinessException {
        //BJCA在此处上传文件并设置签章位置,坐标和关键字不可同时设置
        SignFileUploadVO signFileUploadVO = noSecretService.getSignFileUploadVO(userId, bizSn, fileBasePrefix + informPath, SignFileUploadVO.USER_TYPE_PH, 525f, 105f, null);
        SignAccountVO signAccountVO = this.getSignAccount(userId, "");
        PreSignResultVO vo = esignService.preSignPdf(signFileUploadVO, signAccountVO);
        return PreSignResultBO.builder()
                .uniqueId(vo.getUniqueId())
                .faceUrl(vo.getUrl())
                .build();
    }

    public void signPh(Long pharmacistId, String pin) throws BusinessException {
        EsignNoSecret esignNoSecret = noSecretService.saveEsignNoSecret(pharmacistId, 1);
        String bizSn = esignNoSecret.getId().toString();
        PreSignResultBO preSignResultBO = this.preSignRecipelPh(pharmacistId, bizSn);
        ISignService.SealPosVO sealPosVO = signService.getSealPosConfig().getNsPosPh();
        // e签宝在此处上传文件并设置签章位置
        SignFileUploadVO signFileUploadVO =
            noSecretService.getSignFileUploadVO(pharmacistId, bizSn, fileBasePrefix + informPath,
                SignFileUploadVO.USER_TYPE_PH, sealPosVO.getX(), sealPosVO.getY(), esignNoSecret.getCreatedAt());
        signFileUploadVO.setKeyword(sealPosVO.getKeyword());
        SignAccountVO signAccountVO = this.getSignAccount(pharmacistId, preSignResultBO.getUniqueId());
        signAccountVO.setPin(pin);
        esignService.sign(signFileUploadVO, signAccountVO);
    }

    /**
     * 药师开通免密签署
     * 
     * @param paramBO
     * @throws BusinessException
     */
    public void signPhNoSecret(NoSecretParamBO paramBO) throws BusinessException {
        String bizSn = noSecretService.getNoSecretBizSn(paramBO.getSignUserId(), 1);
        ISignService.SealPosVO sealPosVO = signService.getSealPosConfig().getNsPosPh();
        // e签宝在此处上传文件并设置签章位置
        SignFileUploadVO signFileUploadVO = noSecretService.getSignFileUploadVO(paramBO.getSignUserId(), bizSn,
            fileBasePrefix + informPath, SignFileUploadVO.USER_TYPE_PH, sealPosVO.getX(), sealPosVO.getY(), new Date());
        signFileUploadVO.setKeyword(sealPosVO.getKeyword());
        SignAccountVO signAccountVO = this.getSignAccount(paramBO.getSignUserId(), paramBO.getUniqueId());
        signAccountVO.setPin(paramBO.getAuthPwd());
        esignService.sign(signFileUploadVO, signAccountVO);
    }

    private EsignPharmacist getEsignPharmacist(Long pharmacistId) throws BusinessException {

        EsignPharmacist pharmacist = recomEsignPharmacistRepository.findFirstByPharmacistId(pharmacistId);
        if (pharmacist == null) {
            throw new BusinessException(ExceptionCodes.ESIGN_PHARMACIST_USER_NOT_EXIST);
        }
        return pharmacist;
    }


}
